import { Box, RoundedBox, useGizmoContext } from '@react-three/drei';
import { ThreeEvent, useThree } from '@react-three/fiber';
import * as React from 'react';
import { CanvasTexture, Vector2, Vector3 } from 'three';

type GenericProps = {
  font?: string;
  opacity?: number;
  color?: string;
  hoverColor?: string;
  textColor?: string;
  strokeColor?: string;
  onClick?: (e: ThreeEvent<MouseEvent>) => null;
  faces?: string[];
  scale?: number;
};
type FaceTypeProps = { hover: boolean; index: number } & GenericProps;

const colors = { bg: '#f0f0f0', hover: '#999', text: 'black', stroke: 'black' };
const defaultFaces = ['Right', 'Left', 'Top', 'Bottom', 'Front', 'Back'];

const FaceMaterial = ({
  hover,
  index,
  font = 'bold 28px Inter var, Arial, sans-serif',
  faces = defaultFaces,
  color = colors.bg,
  hoverColor = colors.hover,
  textColor = colors.text,
  strokeColor = colors.stroke,
  opacity = 1,
}: FaceTypeProps) => {
  const gl = useThree((state) => state.gl);
  const texture = React.useMemo(() => {
    const canvas = document.createElement('canvas');
    canvas.width = 40;
    canvas.height = 40;
    const context = canvas.getContext('2d')!;
    // 外部
    context.fillStyle = 'rgb(216 216 216 / 30%)';
    context.fillRect(0, 0, canvas.width, canvas.height);
    // 内部
    const padding = 4;
    context.fillStyle = 'white';
    context.fillRect(
      0 + padding,
      0 + padding,
      canvas.width - padding * 2,
      canvas.height - padding * 2,
    );
    context.strokeStyle = strokeColor;
    // context.strokeRect(0, 0, canvas.width, canvas.height);
    context.font = font;
    context.textAlign = 'center';
    context.textBaseline = 'middle';
    context.fillStyle = textColor;
    context.fillText(
      faces[index].toUpperCase(),
      canvas.width / 2,
      canvas.height / 2,
    );
    const texture = new CanvasTexture(canvas);
    const faceRotation = [Math.PI / 2, -Math.PI / 2, Math.PI, 0, 0, 0];
    const rotation = faceRotation[index];
    texture.center = new Vector2(0.5, 0.5);
    rotation && (texture.rotation = rotation);
    return texture;
  }, [index, faces, font, color, textColor, strokeColor]);
  return (
    <meshBasicMaterial
      map={texture}
      map-anisotropy={gl.capabilities.getMaxAnisotropy() || 1}
      attach={`material-${index}`}
      color={hover ? hoverColor : 'white'}
      // transparent
      // opacity={opacity}
    />
  );
};

const FaceCube = (props: GenericProps) => {
  const { tweenCamera } = useGizmoContext();
  const [hover, setHover] = React.useState<number | null>(null);
  const handlePointerOut = (e: ThreeEvent<PointerEvent>) => {
    e.stopPropagation();
    setHover(null);
  };
  const handleClick = (e: ThreeEvent<MouseEvent>) => {
    e.stopPropagation();
    const direction = e.face!.normal;
    const { z } = direction;
    if (Math.abs(z) === 1) {
      const sign = Math.sign(z);
      tweenCamera(new Vector3(0, sign * -1e-6, z));
    } else {
      tweenCamera(direction);
    }
  };
  const handlePointerMove = (e: ThreeEvent<PointerEvent>) => {
    e.stopPropagation();
    setHover(Math.floor(e.faceIndex! / 2));
  };
  return (
    <Box
      onPointerOut={handlePointerOut}
      onPointerMove={handlePointerMove}
      onClick={props.onClick || handleClick}
    >
      {[...Array(6)].map((_, index) => (
        <FaceMaterial
          key={index}
          index={index}
          hover={hover === index}
          {...props}
        />
      ))}
    </Box>
  );
};

export const GizmoViewcube = (props: GenericProps) => {
  const { scale = 60 } = props;
  return (
    <group scale={[scale, scale, scale]}>
      <FaceCube {...props} />
    </group>
  );
};
